// 这里定义三个常量,分别是promise对象的三种状态 pedding, fulfilled , rejected
const PEDDING = "pedding";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise{
    // promise对象是通过new Promise生成的一个实例化对象,
    // 所以我们要写的MyPromise是一个类,它接收一个参数,这个参数是一个函数,这个函数被称为Promise的执行器,这个执行器立即执行    
    constructor(executor){
        // 执行器执行的时候 接收两个参数resolve和reject,这两个参数也是两个函数,它们的作用是改变promise对象的状态并传值给then方法的回调函数
        // resolve将promise对象的状态修改为fulfilled成功状态,reject将promise对象的状态修改为rejected失败状态
        try{
            executor( this.resolve,this.reject )
        } catch(e){
            this.reject(e.message)
        }
        
    }
    // 定义promise对象的默认状态为pedding
    status = PEDDING;
    // 成功之后的值
    value = undefined;
    // 失败之后的值
    reason = undefined;
    successCallback = [];
    failCallback = [];
    //  定义resolve函数,它接收数据并把
    resolve = (value)=>{
        if(this.status!=PEDDING) return;        //一旦状态确定就不可更改
        this.status = FULFILLED;
        this.value = value;
        // 如果this.successCallback的长度大于0,说明在promise对象状态为pedding的时候,就已经在promise对象的then方法定义过状态改变的回调函数了,那就执行吧!
        // 下面reject函数中同理
        if(this.successCallback.length>0){
            this.successCallback.forEach((Callback)=>{
                Callback(this.value)
            })
        }
    }
    //  定义reject函数
    reject = (reason)=>{
        if(this.status!=PEDDING) return;        //一旦状态确定就不可更改
        this.status = REJECTED;
        this.reason = reason;        
        
        if(this.failCallback.length>0){
            this.failCallback.forEach((item)=>{
                item(this.reason)
            })
        }
    }
    // then方法 接收两个函数作为参数,这两个函数会被定义为promise状态更改后需要执行的回调函数
    then(successCallback,failCallback){
        successCallback = successCallback ? successCallback : value=>value;
        failCallback = failCallback ? failCallback : reason=>  {throw reason};
        // console.log(successCallback,failCallback)
        const promise2 = new MyPromise((resolve,reject)=>{
            // 当前状态为成功
            if(this.status == FULFILLED){
                setTimeout(()=>{
                    try{
                        let x = successCallback(this.value);
                        // console.log(x)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e.message)
                    }
                    
                },0);
                                
            }else if(this.status == REJECTED){
            // 当前状态为失败
                setTimeout(()=>{
                    try{
                        let x = failCallback(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                    
                },0);
            }else{
            // 当前状态为pedding(进行中!)
                // 往this.successCallback中添加回调函数可以实现使用then方法多次被调用                
                this.successCallback.push(
                    ()=>{
                        setTimeout(()=>{
                            try{
                                let x = successCallback(this.value)
                                resolvePromise(promise2,x,resolve,reject)
                            }catch(e){
                                reject(e.message)
                            }
                        },0);
                    }
                );
                this.failCallback.push(
                    ()=>{
                        setTimeout(()=>{
                            try{
                                let x = failCallback(this.reason)
                                resolvePromise(promise2,x,resolve,reject)
                            }catch(e){
                                reject(e.message)
                            }
                        },0);
                    }
                );              
            }
        })
        return promise2;
    }

    // catch方法就是then方法的一个别名，它专门用来捕获错误，看起来只传递了一个回调函数，实际上把它的回调函数传给了then方法的第二个参数
    catch(callBack){
        return this.then(undefined,callBack);
    }

    // finally方法的作用：
    // 1.是无论当前promise对象最终成功还是失败都会调用finally方法，并且它还可以链式调用then方法来拿到当前promise对象的值
    // 2.finally方法的返回值如果是promise2对象，那么它链式调用的then方法会等待它返回值的promise2对象执行完成，但它链式调用的then方法接收到的值是当前promise对象的值
    finally(Callback){
        // return MyPromise.resolve( Callback() ).then()
        return this.then((value)=>{            
            let end = MyPromise.resolve( Callback() ).then(()=>{return value});
            // console.log(end);
            return end;           
        },reason=> {            
            return MyPromise.resolve( Callback() ).then(()=>{throw reason})
        });
    }

    
    // finaly(Callback){
    //     return this.then((value)=>{   
            
    //         return chuli( Callback(),value );   
    //     },reason=> {
    //         Callback();
    //         chuli();
    //         throw reason
            
    //     });
    //     function chuli(x){
    //         if(x instanceof MyPromise){
    //             x.then(()=>{},()=> {});
    //         }
    //     }     
    // }

    static all(array){
        let result = [];
        let num = 0;    //计数器
        return new MyPromise((resolve,reject)=>{
            
            function addResult(index,value){
                num++;
                result[index] = value;
                // 当计数器等于传入数组的长度时，表明传入的所有promise对象或值都已经执行完毕，可以将当前promise对象状态改变为成功，并返回所有值。
                if(num == array.length){
                    resolve(result);
                }
            }
            for(let i=0;i<array.length;i++){
                if(array[i] instanceof MyPromise){
                    // 传入的数组元素执行成功就往result里面添加，有一个失败则将当前promise对象状态改变为失败，并返回失败原因。
                    array[i].then((value)=>{addResult(i,value)} , (reason)=>{ reject(reason) })
                }else{
                    addResult(i,array[i])
                }
            }
        })        
    }

    static resolve(x){
        if( x instanceof MyPromise ){
            return x;
        }else{
            return new MyPromise((resolve,reject)=>{
                resolve(x);
            })            
        }
    }
}

// 封装一个方法判断第一个then方法的回调函数返回值是普通值还是promise对象，拿到最终值 改变promise2的状态并把值传出去
function resolvePromise(promise2,x,resolve,reject){
    if( x == promise2 ) { 
        // 判断第一个then方法的回调函数返回值不能是promise2本身，否则就会发生promise对象的循环调用，我测试了很久发现这个promise2对象的状态将永远为pedding，永远不会被改变！ 
        // 也就是链式调用在此步断掉了，所以必须做这个判断
        return reject( new TypeError("chaining circle are detected for promise #<Promise>") ) 
    }
    if( x instanceof MyPromise ){
        // x是promise
        x.then( (value)=>resolve(value),(reason)=>reject(reason) )
    }else{
        // x是普通值
        resolve(x);
    }
}
module.exports = MyPromise;